﻿using HCLSMod;
using LModBusRTU;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
using ZLSerialPortBusLib.SerialPortBus;

/*-------------------------------------------------------------------------------------------
对象继承关系：
1、串口对象继承关系（SerialPortBase.cs）
                ZLErrorRecord （记录错误）
                    |
              ZLSerialPortBase（串口收发数据管理器，不管理Modbus命令）
                    |
           ZLSerialPortModbusToolBase（Modbus命令的串口总线管理模块）

2、总线管理对象（PLCManagerBase.cs）
ZLPLCNodeInfo：序列化的节点信息，用于数据传递；
ZLPLCModbusNode：基于Modbus协议的PLC节点，处理Modbus命令集寄存器映射；
ZLPLCBusOnSerialPort：基于串口的Modbus总线对象，管理PLC列表集读取或写入PLC寄存器；

-------------------------------------------------------------------------------------------*/

namespace ZLSerialPortBusLib
{
    /// <summary>
    /// 记录系统事件到日志中
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="EventType"></param>
    /// <param name="fErrorInfo"></param>
    public delegate void SystemEventLog(object sender, EventLogEntryType EventType, string fErrorInfo);

    /// <summary>
    /// 可以作为构件使用的窗体接口
    /// </summary>
    public interface ZLFMFormModel
    {
        /// <summary>
        /// 设置窗体的父窗体
        /// </summary>
        /// <param name="fParent"></param>
        void SetParent(Control fParent);
        /// <summary>
        /// 组件关闭
        /// </summary>
        void CloseAll();
    }
    /// <summary>
    /// 网络协议类型
    /// </summary>
    public enum TNetProtocolType
    {
        None = 0,
        TCP = 1,
        UDP = 2,
    }
    /// <summary>
    /// 命令管道专用的命令代码
    /// </summary>
    public enum SerialPortNPCommand
    {
        None = 0,
        GetPLCAddrList = 1,       // 获得PLC地址列表
        GetPLCInfo = 2,         // 获得指定PLC的描述信息
        GetPLCData = 3,         // 获得指定PLC的寄存器数据
        SetPLCData = 4,         // 设置PLC信息
        GetBusInfo = 5,         // 获得总线信息
        SetBusInfo = 6,         // 设置总线信息
        RestartBus = 7,         // 重启总线
        RestartPLC = 8,         // 刷新所有PLC数据
    }

    /// <summary>
    /// 基于串口的总线配置信息，每个串口为一个总线
    /// </summary>
    public interface ISerialPortBusCFG
    {
        /// <summary>
        /// 端口号，字符串；格式：COM[XXX]
        /// </summary>
        string PortName { get; set; }
        /// <summary>
        /// 端口速度；例如：9600
        /// </summary>
        int Baudrate { get; set; }
        /// <summary>
        /// CRC大小头设置
        /// </summary>
        TCRC16Endian CRCEndian { get; set; }
        /// <summary>
        /// RS485的RSCON使用RTS还是DTR
        /// </summary>
        TSerialRSCON RSConPORT { get; set; }
        /// <summary>
        /// 总线下PLC数量
        /// </summary>
        int PLCCount { get; }
        /// <summary>
        /// 总线下PLC信息列表
        /// </summary>
        string[] PLCInfoList { get; set; }

        bool AutoStar { get; set; }
        /// <summary>
        /// 配置文件名称
        /// </summary>
        /// <returns></returns>
        string CFGFile { get; }
        /// <summary>
        /// 总线序号
        /// </summary>
        /// <returns></returns>
        int PortBusIdx { get; }
        /// <summary>
        /// 总线提供的命令管道的名称字符串
        /// </summary>
        string PipeName { get; }
    }
    /// <summary>
    /// 串口总线配置管理器
    /// </summary>
    public class SerialPortBusConfig : ISerialPortBusCFG
    {
        #region 属性及成员
        /// <summary>
        /// INI文件对象
        /// </summary>
        protected IniFile fCFGFile { get; set; }
        /// <summary>
        /// INI中的分段名称
        /// </summary>
        protected const string fSegment = "SerialPortBus";
        /// <summary>
        /// 总线索引号
        /// </summary>
        protected int fBusIndex = 0;
        /// <summary>
        /// 串口波特率
        /// </summary>
        protected int fBaudrate = 9600;
        /// <summary>
        /// 串口波特率
        /// </summary>
        public int Baudrate
        {
            get { return fBaudrate; }
            set
            {
                fBaudrate = value;
                SaveInfoToCFG();
            }
        }
        /// <summary>
        /// CRC校验码高低位顺序
        /// </summary>
        protected TCRC16Endian fCRCEndian = TCRC16Endian.Big;
        /// <summary>
        /// CRC校验码高低位顺序
        /// </summary>
        public TCRC16Endian CRCEndian
        {
            get { return fCRCEndian; }
            set
            {
                fCRCEndian = value;
                SaveInfoToCFG();
            }
        }
        /// <summary>
        /// 总线上PLC数量
        /// </summary>
        public int PLCCount { get { return fPLCInfoList.Count; } }
        /// <summary>
        /// 总线上PLC配置字符串列表
        /// </summary>
        protected List<string> fPLCInfoList = new List<string>();
        /// <summary>
        /// 总线上PLC配置字符串列表
        /// </summary>
        public string[] PLCInfoList
        {
            get { return fPLCInfoList.ToArray(); }
            set
            {
                fPLCInfoList = new List<string>(value);
                SaveInfoToCFG();
            }
        }
        /// <summary>
        /// 串口端口名称
        /// </summary>
        protected string fPortName = "COM1";
        /// <summary>
        /// 串口端口名称
        /// </summary>
        public string PortName
        {
            get { return fPortName; }
            set
            {
                fPortName = value;
                SaveInfoToCFG();
            }
        }
        /// <summary>
        /// 串口总线是否自动启动，True：创建串口总线的时候自动启动总线
        /// </summary>
        public bool AutoStar
        {
            get { return fCFGFile.ReadIniKeys(fSegment, "AutoStar_" + fBusIndex.ToString(), "0") != "0"; }
            set { fCFGFile.WriteIniKeys(fSegment, "AutoStar_" + fBusIndex.ToString(), value ? "1" : "0"); }
        }
        /// <summary>
        /// 总线在总线列表中的序号
        /// </summary>
        public int PortBusIdx { get { return fBusIndex; } }
        /// <summary>
        /// 命令管道名称
        /// </summary>
        public string PipeName { get { return "BusSerPN[" + fPortName + "]"; } }
        /// <summary>
        /// RS485收发控制端口 
        /// </summary>
        protected TSerialRSCON fRSConPORT = TSerialRSCON.None;
        /// <summary>
        /// RS485收发控制端口 
        /// </summary>
        public TSerialRSCON RSConPORT
        {
            get { return fRSConPORT; }
            set
            {
                fRSConPORT = value;
                SaveInfoToCFG();
            }
        }
        /// <summary>
        /// 配置文件名称
        /// </summary>
        public string CFGFile { get { return fCFGFile.IniFileName; } }

        #endregion

        #region 构建函数

        public SerialPortBusConfig(IniFile fCFGFileX, int fBusIdx)
        {
            fCFGFile = fCFGFileX;
            fBusIndex = fBusIdx;
            LoadBusInfoFromCFG();
        }

        public SerialPortBusConfig(string fCFGFileName, int fBusIdx)
        {
            if (File.Exists(fCFGFileName))
                fCFGFile = new IniFile(fCFGFileName);
            else
                fCFGFile = new IniFile(IniFile.NewIniFileName());
            fBusIndex = fBusIdx;
            LoadBusInfoFromCFG();
        }
        
        /// <summary>
        /// 将所有配置写入到配置文件中
        /// </summary>
        protected void SaveInfoToCFG()
        {
            string fBusInfo = string.Format("{0},{1},{2},{3}",
                fPortName,
                fBaudrate.ToString(),
                ((int)fRSConPORT).ToString(),
                ((int)fCRCEndian).ToString());
            fCFGFile.WriteIniKeys(fSegment, "BusInfo_" + fBusIndex.ToString(), fBusInfo);

            string fPLCInfo = "";
            foreach (string fInfo in fPLCInfoList)
                fPLCInfo += fInfo + ";";
            fPLCInfo = fPLCInfo.TrimEnd(';');
            fCFGFile.WriteIniKeys(fSegment, "BusPLCInfo_" + fBusIndex.ToString(), fPLCInfo);
        }
        /// <summary>
        /// 从INI文件中导入所有总线配置
        /// </summary>
        protected void LoadBusInfoFromCFG()
        {
            string fStrTemp = fCFGFile.ReadIniKeys(fSegment, "BusInfo_" + fBusIndex.ToString(), "");
            int fiCRCEndian = 0;
            int fiRSCON = 0;
            if (fStrTemp.IndexOf(',') > 0)
            {
                string[] fInfoList = fStrTemp.Split(',');
                if ((fInfoList.Length == 4)
                    && int.TryParse(fInfoList[1], out fBaudrate)
                    && int.TryParse(fInfoList[2], out fiRSCON)
                    && int.TryParse(fInfoList[3], out fiCRCEndian))
                {
                    fPortName = fInfoList[0];
                    fRSConPORT = (TSerialRSCON)fiRSCON;
                    fCRCEndian = (TCRC16Endian)fiCRCEndian;
                }
            }
            fStrTemp = fCFGFile.ReadIniKeys(fSegment, "BusPLCInfo_" + fBusIndex.ToString(), "");
            fPLCInfoList = new List<string>(fStrTemp.Split(';'));
        }
        #endregion
    }

}